package top.hcode.hoj.service.admin;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import top.hcode.hoj.dao.CollQuestionDao;
import top.hcode.hoj.dao.CollQuestionOptionDao;
import top.hcode.hoj.pojo.dto.coll.AdmCollQuestionJsonImportDTO;
import top.hcode.hoj.pojo.entity.coll.CollQuestion;
import top.hcode.hoj.pojo.entity.coll.CollQuestionOption;
import top.hcode.hoj.pojo.form.AdmCollGetQuestionForm;
import top.hcode.hoj.pojo.form.AdmQueryCollQuestionForm;
import top.hcode.hoj.utils.Constants;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author orangej
 * @since 2025-02-09
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AdminCollQuestionService {
    private final CollQuestionDao questionDao;
    private final CollQuestionOptionDao optionDao;

    public IPage<CollQuestion> getCollQuestionList(AdmQueryCollQuestionForm form) {
        IPage<CollQuestion> pager = new Page<>(form.getCurrentPage(), form.getLimit());

        QueryWrapper<CollQuestion> query = new QueryWrapper<>();
        if (Strings.isNotBlank(form.getKeyword())) {
            query.like("title", form.getKeyword());
        }
        query.eq(Strings.isNotBlank(form.getCollId()), "coll_id", form.getCollId());
        query.orderByAsc("sort_order");
        questionDao.page(pager, query);
        return pager;
    }

    public CollQuestion getCollQuestion(Long id) {
        return questionDao.getById(id);
    }


    @Transactional
    public CollQuestion saveCollQuestion(CollQuestion dbo) {
        if (Strings.isBlank(dbo.getCollId())) {
            dbo.setCollId(Constants.DEFAULT_COLL_ID);
        }

        if (dbo.getSortOrder() == null || dbo.getSortOrder() == 0) {
            Integer max = questionDao.getBaseMapper().selectMaxSortOrder(dbo.getCollId());
            dbo.setSortOrder(max == null ? 1 : max + 1);
        }

        questionDao.saveOrUpdate(dbo);

        // 解析选择题选项和答案
        if (Constants.QuestionType.isChoice(dbo.getQuestionType())) {
            String content = dbo.getTitle();
            String optionsTag = "===> options";
            String answerTag = "===> answer";

            String title = StrUtil.subBefore(content, optionsTag, true).trim();
            Validator.validateNotEmpty(title, "题目内容不能为空");
            dbo.setTitle(title);
            questionDao.saveOrUpdate(dbo);

            List<String> optionContentList = extractOptions(content);
            Validator.validateTrue(Lang.isNotEmpty(optionContentList), "题目选项不能为空");
            String answer = StrUtil.subAfter(content, answerTag, true).trim().toUpperCase();
            Validator.validateNotEmpty(answer, "题目答案不能为空");

            // 更新选项内容
            List<CollQuestionOption> oList = optionDao.queryByQuestionId(dbo.getId());

            // 删除多余选项
            if (oList.size() > optionContentList.size()) {
                for (int i = 0; i < oList.size(); i++) {
                    if (i >= optionContentList.size()) {
                        optionDao.removeById(oList.get(i).getId());
                    }
                }
            }
            for (int i = 0; i < optionContentList.size(); i++) {
                String num = String.valueOf((char) ('A' + i));

                CollQuestionOption option = oList.stream().filter(o -> o.getOptionNum().equals(num)).findFirst().orElse(new CollQuestionOption());
                option.setCollId(dbo.getCollId());
                option.setQuestionId(dbo.getId());
                option.setOptionNum(num);
                option.setContent(optionContentList.get(i));
                option.setIsAnswer(answer.contains(num));
                optionDao.saveOrUpdate(option);
            }
        } else if (ObjUtil.equals(dbo.getQuestionType(), Constants.QuestionType.BLANK.getType())) {
            String content = dbo.getTitle();
            String answerTag = "===> answer";

            // 解析填空题题干
            String title = StrUtil.subBefore(content, answerTag, true).trim();
            Validator.validateNotEmpty(title, "题目内容不能为空");
            dbo.setTitle(title);
            questionDao.saveOrUpdate(dbo);

            // 解析填空题答案
            String answer = StrUtil.subAfter(content, answerTag, true).trim();
            Validator.validateNotEmpty(answer, "题目答案不能为空");

            // 只保留1个答案
            List<CollQuestionOption> oList = optionDao.queryByQuestionId(dbo.getId());
            if (oList.size() > 1) {
                for (int i = 0; i < oList.size(); i++) {
                    if (i >= 1) {
                        optionDao.removeById(oList.get(i).getId());
                    }
                }
            }

            // 保存答案内容
            CollQuestionOption option = oList.stream().findFirst().orElse(new CollQuestionOption());
            option.setCollId(dbo.getCollId());
            option.setQuestionId(dbo.getId());
            option.setOptionNum("1");
            option.setContent(answer);
            option.setIsAnswer(true);
            option.setSortOrder(1);
            optionDao.saveOrUpdate(option);
        }

        return dbo;
    }

    /**
     * 解析选择题选项和答案
     */
    public static List<String> extractOptions(String input) {
        List<String> options = new ArrayList<>();
        // 使用正则表达式匹配选项部分
        Pattern pattern = Pattern.compile("===> options\\s*(.*?)(===> answer)", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(input);

        if (matcher.find()) {
            // 提取选项部分
            String optionsSection = matcher.group(1).trim();
            // 按选项标识符分割选项，选项标识符为字母后跟点号（如A.、B.等）
            String[] optionBlocks = optionsSection.split("(?m)^[A-D]\\.");
            for (int i = 1; i < optionBlocks.length; i++) { // 跳过第一个空块
                // 去掉多余的空格并添加到结果列表
                String optionContent = optionBlocks[i].trim();
                options.add(optionContent);
            }
        }

        return options;
    }

    @Transactional
    public void deleteCollQuestion(Long id) {
        questionDao.removeById(id);
        optionDao.removeByQuestionId(id);
    }

    // 查询单个题目信息，可以根据id，也可以根据collId，questionTypeList，sortOrder查询，方便查询上一题、下一题
    public CollQuestion getCollQuestion(AdmCollGetQuestionForm form) {
        if (Strings.isNotBlank(form.getId())) {
            return questionDao.getById(form.getId());
        }

        return questionDao.lambdaQuery()
                .eq(Strings.isNotBlank(form.getCollId()), CollQuestion::getCollId, form.getCollId())
                .in(Strings.isNotBlank(form.getQuestionTypes()), CollQuestion::getQuestionType,
                        StrUtil.split(form.getQuestionTypes(), ","))
                .eq(form.getSortOrder() != null, CollQuestion::getSortOrder, form.getSortOrder())
                .one();
    }


    /**
     * 获取题目详情，方便编辑，将题干和选项合并到一个markdown文件里，格式：
     * <p>
     * 这是一道选择题，请开始输入题干内容吧。
     * <p>
     * 请勿编辑以“===”开头的内容
     * 请勿编辑选项前的字母编码
     * 在最后一行输入正确答案编号，可以多选，例如：ABC
     * ===> options
     * A. 选项A的内容
     * B. 选项B的内容
     * C. 选项C的内容
     * D. 选项D的内容
     * ===> answer
     * A
     *
     * @param form 请求题目详情
     * @return 题目详情
     */
    public CollQuestion getCollQuestionDetailEdit(AdmCollGetQuestionForm form) {
        CollQuestion q = getCollQuestion(form);

        // 处理选择题，将选项和答案合并到题干
        if (Constants.QuestionType.isChoice(q.getQuestionType())) {
            List<CollQuestionOption> oList = optionDao.queryByQuestionId(q.getId());
            String content = q.getTitle();
            content += "\r\n\r\n===> options\r\n";

            String answer = "";
            for (CollQuestionOption o : oList) {
                content += o.getOptionNum() + ". " + o.getContent() + "\r\n";
                if (BooleanUtil.isTrue(o.getIsAnswer())) {
                    answer += o.getOptionNum();
                }
            }
            content += "===> answer\r\n" + answer;

            q.setTitle(content);
        } else if (ObjUtil.equals(Constants.QuestionType.BLANK.getType(), q.getQuestionType())) {
            // 处理填空题，将答案合并到题干
            List<CollQuestionOption> oList = optionDao.queryByQuestionId(q.getId());
            String content = q.getTitle();
            content += "\r\n\r\n===> answer\r\n";
            for (CollQuestionOption o : oList) {
                content += o.getContent() + "\r\n";
            }
            q.setTitle(content);
        }

        return q;
    }


    /**
     * 导入的题目数据格式：[{title, score, questionType, options:[{num, content, isCorrect}]}]
     * @param dto
     */
    @Transactional
    public void importCollQuestionJson(AdmCollQuestionJsonImportDTO dto) {
        int sortOrder = questionDao.getMaxSortOrder(dto.getCollId());

        for (NutMap q : dto.getQuestions()) {
            // [{title, score, questionType, options:[{num, content, isCorrect}]}]
            String title = q.getString("title");
            Validator.validateNotEmpty(title, "题目标题不能为空");

            BigDecimal score = q.getAs("score", BigDecimal.class);
            Validator.validateNotNull(score, "题目分数不能为空");
            Validator.validateTrue(score.compareTo(BigDecimal.ZERO) > 0, "题目分数必须大于0");
            Validator.validateTrue(score.compareTo(BigDecimal.valueOf(100)) <= 0, "题目分数不能大于100");

            Integer questionType = q.getInt("questionType");
            Validator.validateNotNull(questionType, "题目类型不能为空");
            Validator.validateTrue(CollUtil.newArrayList(1, 2, 3).contains(questionType), "题目类型只能是选择题或填空题");

            // save question
            CollQuestion question = new CollQuestion();
            question.setTitle(title);
            question.setScore(score);
            question.setQuestionType(questionType);
            question.setSortOrder(++sortOrder);
            question.setCollId(dto.getCollId());
            questionDao.saveOrUpdate(question);

            // 处理选择题选项
            if (Constants.QuestionType.isChoice(questionType)) {
                List<NutMap> oList = q.getList("options", NutMap.class);
                List<CollQuestionOption> optionList = new ArrayList<>();
                for (NutMap o : oList) {
                    String num = o.getString("num");
                    Validator.validateNotEmpty(num, "选项序号不能为空");
                    Validator.validateTrue(CollUtil.newArrayList("A", "B", "C", "D", "E", "F", "G", "H").contains(num),
                            "选项序号只能是A、B、C、D、E、F、G、H");

                    String content = o.getString("content");
                    Validator.validateNotEmpty(content, "选项内容不能为空");

                    Boolean isCorrect = false;
                    if (o.containsKey("isCorrect")) {
                        isCorrect = o.getBoolean("isCorrect");
                    }

                    // save option
                    CollQuestionOption option = new CollQuestionOption();
                    option.setCollId(dto.getCollId());
                    option.setQuestionId(question.getId());
                    option.setOptionNum(num);
                    option.setContent(content);
                    option.setIsAnswer(isCorrect);
                    optionList.add(option);
                }
                optionDao.saveOrUpdateBatch(optionList);
            } else if (ObjUtil.equals(questionType, Constants.QuestionType.BLANK.getType())) {
                // 处理填空题答案
                String answer = q.getString("answer");
                Validator.validateNotEmpty(answer, "填空题答案不能为空");

                // 保存答案内容
                CollQuestionOption option = new CollQuestionOption();
                option.setCollId(dto.getCollId());
                option.setQuestionId(question.getId());
                option.setOptionNum("1");
                option.setContent(answer);
                option.setIsAnswer(true);
                option.setSortOrder(1);
                optionDao.saveOrUpdate(option);
            }
        }
    }

}
